package io.noties.markwon.base.image.load

import android.graphics.drawable.Animatable
import android.graphics.drawable.Drawable
import com.bumptech.glide.RequestBuilder
import com.bumptech.glide.request.target.CustomTarget
import com.bumptech.glide.request.target.Target
import com.bumptech.glide.request.transition.Transition
import io.noties.markwon.base.image.AsyncDrawable
import io.noties.markwon.base.image.AsyncRecycleDrawable
import io.noties.markwon.base.image.DrawableUtils
import io.noties.markwon.base.image.size.ImageAutoSizePreLoadResolverDef
import timber.log.Timber
import java.lang.Exception

interface AsyncRecycleDrawableLoader : AsyncDrawableLoader {
     fun preLoad(drawable: AsyncRecycleDrawable)
     fun load2(drawable: AsyncRecycleDrawable)
     fun recycle(drawable: AsyncRecycleDrawable)
}

interface ExtGlideStore {
     val placeholder: Drawable?
     val imagePreLoadSizeResolverDef: ImageAutoSizePreLoadResolverDef

     fun preLoad(drawable: AsyncRecycleDrawable): RequestBuilder<Drawable>
     fun clearMemoryCache()
     fun load(drawable: AsyncDrawable): RequestBuilder<Drawable>
     fun cancel(target: Target<*>)
}

class GlideAsyncRecycleDrawableLoader constructor(private val glideStore: ExtGlideStore) :
     AsyncRecycleDrawableLoader {
     private val cache= mutableMapOf<AsyncDrawable, Target<Drawable>>()

     override fun load2(drawable: AsyncRecycleDrawable) {
          if (cache.containsKey(drawable)) return
          val target: Target<Drawable> = AsyncDrawableTarget(drawable)
          cache[drawable] = target
          try {
               Timber.d("AsyncRecycleDrawableLoader load2:%${ drawable.destination}")
               glideStore.load(drawable).into(target)
          } catch (e: Exception) {
               Timber.d(e)
          }
     }

     override fun load(drawable: AsyncDrawable) {
          Timber.d("AsyncRecycleDrawableLoader empty load:${drawable.destination}" )
     }

     override fun recycle(drawable: AsyncRecycleDrawable) {
          Timber.d("AsyncRecycleDrawableLoader recycle:${drawable.destination}" )
          //glideStore.cancel(target);
     }

     override fun cancel(drawable: AsyncDrawable) {
          val target = cache.remove(drawable)
          if (target != null) {
               Timber.d("AsyncRecycleDrawableLoader cancel:%s", drawable.destination)
               glideStore.cancel(target)
          }
     }

     override fun placeholder(): Drawable? {
          return null
     }

     override fun preLoad(drawable: AsyncRecycleDrawable) {
          Timber.d("AsyncRecycleDrawableLoader preLoad:%s", drawable.destination)
          val rect = glideStore.imagePreLoadSizeResolverDef.resolveImageSize(drawable)
          if (!rect.isEmpty) {
               drawable.preLoad(glideStore.placeholder, rect)
               return
          }
          drawable.preLoad(glideStore.placeholder)
          try {
               glideStore.preLoad(drawable).into(AsyncPreLoadDrawableTarget(drawable))
          } catch (e: Exception) {
               Timber.d(e)
          } /*finally {
                glideStore.cancel(target);
            }*/
     }

     private class AsyncPreLoadDrawableTarget  constructor(var drawable: AsyncRecycleDrawable?) :
          CustomTarget<Drawable>() {

          override fun onResourceReady(resource: Drawable, transition: Transition<in Drawable?>?) {
               Timber.d("AsyncRecycleDrawableLoader preLoad:${drawable?.destination} onResourceReady,isAttached:${drawable?.isAttached}"   )
               if (drawable?.isAttached == true) {
                    DrawableUtils.applyIntrinsicBoundsIfEmpty(resource)
                    drawable?.preLoad(resource.copyBounds())
               }
               drawable = null
          }

          override fun onLoadCleared(placeholder: Drawable?) {}
     }

     private inner class AsyncDrawableTarget  constructor(private val drawable: AsyncDrawable) :
          CustomTarget<Drawable>() {
          override fun onResourceReady(resource: Drawable, transition: Transition<in Drawable>?) {
               if (cache.remove(drawable) != null) {
                    Timber.d("AsyncRecycleDrawableLoader AsyncDrawableTarget Load:${drawable.destination} onResourceReady,isAttached:${drawable.isAttached}")
                    if (drawable.isAttached) {
                         DrawableUtils.applyIntrinsicBoundsIfEmpty(resource)
                         drawable.setResult( resource )
                         if (resource is Animatable && !(resource as Animatable).isRunning) {
                              (resource as Animatable).start()
                         }
                    }
               }
          }

          override fun onLoadStarted(placeholder: Drawable?) {
               if (placeholder != null && drawable.isAttached) {
                    DrawableUtils.applyIntrinsicBoundsIfEmpty(placeholder)
                    drawable.setResult(placeholder)
               }
          }

          override fun onLoadFailed(errorDrawable: Drawable?) {
               if (cache.remove(drawable) != null) {
                    if (errorDrawable != null && drawable.isAttached) {
                         DrawableUtils.applyIntrinsicBoundsIfEmpty(errorDrawable)
                         drawable.setResult( errorDrawable)
                    }
               }
          }

          override fun onLoadCleared(placeholder: Drawable?) {
               // we won't be checking if target is still present as cancellation
               // must remove target anyway
               if (drawable.isAttached) {
                    drawable.clearResult()
               }
          }
     }
}